/**
 *  Copyright 23.07.2012 oliver.keusemann@googlemail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package de.xelerity.geolessi.types.poi;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.xelerity.geolessi.preferences.Preferences;
import de.xelerity.geolessi.srv.impl.poi.bahn.HalteStelleNextAbfahrtGetterImpl;
import de.xelerity.geolessi.types.mixed.Koordinate;
import de.xelerity.geolessi.types.verbindung.Linie;
import de.xelerity.geolessi.types.verbindung.Linien;
import de.xelerity.geolessi.types.verbindung.Richtung;

public class HalteStellenListe implements Serializable {

	private static final long serialVersionUID = 4741659L;

	private static Log log = LogFactory.getLog(HalteStellenListe.class);

	private List<HalteStelle> halteStellen;

	private Koordinate CenterSearch;

	private long SearchRadiusM;

	public HalteStellenListe() {
		halteStellen = new ArrayList<HalteStelle>();
	}

	public void addHalteStelle(HalteStelle pHalteStelle) {
		if (pHalteStelle != null) {
			halteStellen.add(pHalteStelle);
		}
	}

	public List<HalteStelle> getHalteStellen() {
		return halteStellen == null ? new ArrayList<HalteStelle>() : halteStellen;
	}

	public void setHalteStellen(List<HalteStelle> halteStellen) {
		this.halteStellen = halteStellen;
	}

	public Koordinate getCenterSearch() {
		return new Koordinate(CenterSearch);
	}

	public void setCenterSearch(Koordinate searchCenter) {
		CenterSearch = new Koordinate(searchCenter);
	}

	public long getSearchRadiusM() {
		return SearchRadiusM;
	}

	public void setSearchRadiusM(long searchRadiusM) {
		SearchRadiusM = searchRadiusM;
	}

	public HalteStelle getFirstHSWithType(long pHalteStelleType) {
		for (HalteStelle halteStelle : getHalteStellen()) {
			if (halteStelle.getHalteStelleTyp() == pHalteStelleType) {
				return halteStelle;
			}
		}
		return null;
	}

	public List<HalteStelle> getAllHSWithType(long pHalteStelleType) {
		ArrayList<HalteStelle> res = new ArrayList<HalteStelle>();
		for (HalteStelle halteStelle : getHalteStellen()) {
			if (halteStelle.getHalteStelleTyp() == pHalteStelleType) {
				res.add(halteStelle);
			}
		}
		return res;
	}

	public List<HalteStelle> getHSWithType(long pHalteStelleType) {
		ArrayList<HalteStelle> res = new ArrayList<HalteStelle>();
		int found;
		HashMap<Integer, List<HalteStelle>> treffer = new HashMap<Integer, List<HalteStelle>>();

		for (int i = 0; i < getHalteStellen().size(); i++) {
			HalteStelle HS = getHalteStellen().get(i);

			found = 0;
			for (Linie L : HS.getLinien()) {
				if (L.getLinienTyp() == pHalteStelleType) {
					found++;
				}
			}
			// getHalteStellen().get(i) hat found treffer
			if (found > 0) {
				List<HalteStelle> list = treffer.get(found);
				if (list == null) {
					list = new ArrayList<HalteStelle>();
				}
				list.add(getHalteStellen().get(i));
				treffer.put(found, list);
			}
		}

		if (treffer.size() > 0) {
			ArrayList<Integer> AL = new ArrayList<Integer>();
			AL.addAll(treffer.keySet());
			Collections.sort(AL);
			Collections.reverse(AL);
			for (Integer I : AL) {
				res.addAll(treffer.get(I));
			}
		}

		return res;
	}

	public void clean() {
		// remove HS without Lines
		List<HalteStelle> compactedHS = new ArrayList<HalteStelle>();
		for (HalteStelle HS : getHalteStellen()) {
			if (HS.getLinien().size() > 0) {
				compactedHS.add(HS);
			}
		}
		setHalteStellen(compactedHS);

		// sort Lines
		for (HalteStelle HS : getHalteStellen()) {
			if (HS.getLinien().size() > 0) {
				Linien linien = HS.getLinien();
				linien.sort();
			}
		}
	}

	public HalteStelle halteStelleExists(HalteStelle pHS) {
		if (pHS == null || !pHS.getKoordinate().isValid()) {
			return null;
		}

		for (HalteStelle halteStelle : getHalteStellen()) {

			if (!halteStelle.getKoordinate().isValid()) {
				continue;
			}
			/*
			 * Wenn HalteStellen exakt am gleichen Ort (bzw. d < HSISEQUALDISTANCE), sind Haltestellen
			 * gleich
			 */
			if (pHS.getKoordinate().distance(halteStelle.getKoordinate()) <= Preferences.HSISEQUALDISTANCE) {
				return halteStelle;
			}

			/*
			 * Wenn HalteStellen 'ziemlich' am gleichen Ort (bzw. d < HSISEQUALDISTANCE1) und die
			 * Liniensignatur uebereinstimmt, sind Haltestellen gleich / if
			 * (pHS.getKoordinate().distance(halteStelle.getKoordinate()) <=
			 * Preferences.HSISEQUALDISTANCE1) {
			 * 
			 * if (pHS.hashCodeL1() == halteStelle.hashCodeL1()) { return halteStelle; } }
			 */

		}
		// Keine aehnliche HS gefunden
		return null;
	}

	// B nach A
	private boolean doClusterHS(HalteStelle A, HalteStelle B, long haltestelleType) {

		if (A.getKoordinate().equals(B.getKoordinate())) {
			return false;
		}

		if (((A.getHalteStelleTyp() & haltestelleType) == 0L) || ((B.getHalteStelleTyp() & haltestelleType) == 0L)) {
			return false;
		}

		long clusterDistance = 200;
		// long clusterDistanceA = 200;
		// long clusterDistanceB = 200;

		if (haltestelleType == HalteStelle.HalteStelleTyp_UBAHN) {
			clusterDistance = 225;
		} else if (haltestelleType == HalteStelle.HalteStelleTyp_SBAHN) {
			clusterDistance = 225;
		} else if (haltestelleType == HalteStelle.HalteStelleTyp_BUS) {
			clusterDistance = 150;
		} else if (haltestelleType == HalteStelle.HalteStelleTyp_ZAHNRAD) {
			clusterDistance = 600;
		} else {
			log.error("Keine Clusterdistance fuer 'haltestelleType' definiert:" + haltestelleType);
		}

		boolean moveLinien = false;
		// System.out.println(A.getKoordinate().distance(B.getKoordinate()) + " -- " +
		// clusterDistance);
		if (A.getKoordinate().distance(B.getKoordinate()) < clusterDistance) {

			// B Linie nach A
			log.info("Cluster. Vorher. B> " + B.getTitle() + " " + B.getIDDescAsString() + ":" + B.getLinien().toString()
					+ " --> A>" + A.getTitle() + " " + A.getIDDescAsString() + ":" + A.getLinien().toString());

			moveLinien = A.moveLinien(B, haltestelleType);

			if (!moveLinien) {
				log.info("Nothing moved");
				log.info("-----------------------------------------------------------------------------------------------------------------------------------------------");
			} else {

				log.info("Cluster. Nacher. B> " + B.getTitle() + " " + B.getIDDescAsString() + ":" + B.getLinien().toString()
						+ " --> A>" + A.getTitle() + " " + A.getIDDescAsString() + ":" + A.getLinien().toString());

				log.info("-----------------------------------------------------------------------------------------------------------------------------------------------");

			}
		}
		return moveLinien;
	}

	public void cluster() {

		List<HalteStelle> allHSWithType = null;

		long[] HSTypes = { HalteStelle.HalteStelleTyp_SBAHN, HalteStelle.HalteStelleTyp_UBAHN, HalteStelle.HalteStelleTyp_BUS,
				HalteStelle.HalteStelleTyp_ZAHNRAD };

		boolean move = false;
		List<HalteStelle> alleHalteStellen = null;

		// for (int xx = 0; xx < 20; xx++)
		for (long HSType : HSTypes) {

			move = true;
			for (int sec = 0; ((sec < 30) && move); sec++) {
				clean();
				allHSWithType = getAllHSWithType(HSType);
				move = false;
				for (int i = 0; (i < allHSWithType.size() && !move); i++) {
					HalteStelle A = allHSWithType.get(i);
					for (int j = i + 1; (j < allHSWithType.size() && !move); j++) {
						HalteStelle B = allHSWithType.get(j);
						move = doClusterHS(A, B, HSType);
					}
				}
			}

			move = true;
			for (int sec = 0; ((sec < 30) && move); sec++) {
				clean();
				allHSWithType = getAllHSWithType(HSType);
				move = false;
				for (int i = 0; (i < allHSWithType.size() && !move); i++) {
					HalteStelle A = allHSWithType.get(i);
					alleHalteStellen = getHalteStellen();
					for (int j = 0; (j < alleHalteStellen.size() && !move); j++) {
						HalteStelle B = alleHalteStellen.get(j);
						move = doClusterHS(A, B, HSType);
					}
				}
			}

			move = true;
			for (int sec = 0; ((sec < 30) && move); sec++) {
				clean();
				allHSWithType = getHSWithType(HSType);
				move = false;
				for (int i = 0; (i < allHSWithType.size() && !move); i++) {
					HalteStelle A = allHSWithType.get(i);
					for (int j = i + 1; (j < allHSWithType.size() && !move); j++) {
						HalteStelle B = allHSWithType.get(j);
						move = doClusterHS(A, B, HSType);
					}
				}
			}

			move = true;
			for (int sec = 0; ((sec < 30) && move); sec++) {
				clean();
				allHSWithType = getHSWithType(HSType);
				move = false;
				for (int i = 0; (i < allHSWithType.size() && !move); i++) {
					HalteStelle A = allHSWithType.get(i);
					alleHalteStellen = getHalteStellen();
					for (int j = 0; (j < alleHalteStellen.size() && !move); j++) {
						HalteStelle B = alleHalteStellen.get(j);
						move = doClusterHS(A, B, HSType);
					}
				}
			}
		}

	}

	private boolean HSEqualExhausted(HalteStelle A, HalteStelle B, long haltestelleType) {
		try {

			long time = new java.util.Date().getTime() + Preferences.ServerTimeOffset + (5 * 60 * 1000);

			List<String> AiDs = A.getIDs(HaltestellenIDProvider.BAHN);
			List<NextAbfahrtInfo> resA = new HalteStelleNextAbfahrtGetterImpl().getFirstAbfahrten(AiDs, haltestelleType, time);

			List<String> BiDs = B.getIDs(HaltestellenIDProvider.BAHN);
			List<NextAbfahrtInfo> resB = new HalteStelleNextAbfahrtGetterImpl().getFirstAbfahrten(AiDs, haltestelleType, time);

			return false;
		} catch (Throwable e) {
			log.error("Runtime Error in 'HSEqualExhausted':" + e.getMessage());
			return false;
		}
	}

	public void print() {

		System.out
				.println("\n-----------------------------------------------------------------------------------------------------------------");
		System.out.println("Center      : " + CenterSearch.toString() + "   Radius:" + SearchRadiusM);

		List<HalteStelle> halteStellen = getHalteStellen();
		if (halteStellen == null || halteStellen.size() <= 0) {
			System.out
					.println("-----------------------------------------------------------------------------------------------------------------");
			System.out.println(" Keine Haltestellen gefunden!");
		} else {
			for (HalteStelle HS : halteStellen) {
				System.out
						.println("=================================================================================================================");
				System.out.println(HS.getTitle() + " -- " + HS.getAddressbez() + " -- " + HS.getIDDescAsString() + "-- "
						+ HS.getHalteStelleTypAsString() + " -- " + HS.getKoordinate());
				System.out
						.println("-----------------------------------------------------------------------------------------------------------------");

				Linien linien = HS.getLinien();
				if (linien == null || linien.size() <= 0) {
					System.out.println("Keine Linie gefunden.");
				} else {
					for (Linie L : linien) {
						System.out.println("Line: " + L.getLinienTypAsString() + " " + L.getNummer());
						for (Richtung dirs : L.getDirections()) {
							System.out.println("  ->" + dirs.toString());
						}
					}
				}
			}
		}
		System.out
				.println("-----------------------------------------------------------------------------------------------------------------");

	}

}
